package com.hoolai.qps.mina.string;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.charset.Charset;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

import org.apache.log4j.Logger;
import org.apache.mina.core.service.IoAcceptor;
import org.apache.mina.core.service.IoHandlerAdapter;
import org.apache.mina.core.session.IdleStatus;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.ProtocolCodecFilter;
import org.apache.mina.filter.codec.textline.TextLineCodecFactory;
import org.apache.mina.filter.logging.LoggingFilter;
import org.apache.mina.transport.socket.nio.NioSocketAcceptor;

public class ProxyServer {

    private static final Logger logger = Logger.getLogger(LogicServer.class);

    private final ConcurrentHashMap<Integer, IoSession> logicServers = new ConcurrentHashMap<Integer, IoSession>();

    private static final ConcurrentHashMap<Integer, IoSession> clientGroup = new ConcurrentHashMap<Integer, IoSession>();

    public final static String separator = "!@#";

    public static final String hostName = "192.168.20.13";

    public static void main(String[] args) throws IOException {
        ProxyServer server = new ProxyServer();
        server.start();
        logger.info("proxy server start!");
    }

    public void start() throws IOException {
        //logicProxyServerStart();
        clientProxyServerStart();
        logger.info("init client proxy server finish");
    }

    private void logicProxyServerStart() throws IOException {
        IoAcceptor acceptor = new NioSocketAcceptor();

        acceptor.getFilterChain().addLast("logger", new LoggingFilter());
        acceptor.getFilterChain().addLast("codec",
                new ProtocolCodecFilter(new TextLineCodecFactory(Charset.forName("UTF-8"))));

        acceptor.setHandler(new IoHandlerAdapter() {
            @Override
            public void messageReceived(IoSession session, Object message) throws Exception {
                String msg = message.toString();
                if (msg.startsWith(LogicServer.registerMsg)) {
                    logicServers.put(Integer.valueOf(msg.split(separator)[2]), session);
                    session.write(LogicServer.registerMsg);
                } else {
                    Msg logicMsg = new Msg(message);
                    IoSession ioSession = clientGroup.get(logicMsg.getUid());
                    ioSession.write(logicMsg.toString());
                }
            }

        });

        acceptor.getSessionConfig().setReadBufferSize(2048);
        acceptor.getSessionConfig().setIdleTime(IdleStatus.BOTH_IDLE, 10);
        acceptor.bind(new InetSocketAddress(hostName, 1111));
    }

    private void clientProxyServerStart() throws IOException {

        IoAcceptor acceptor = new NioSocketAcceptor(4);

//        acceptor.getFilterChain().addLast("logger", new LoggingFilter());
//        acceptor.getFilterChain().addLast("codec",
//                new ProtocolCodecFilter(new TextLineCodecFactory(Charset.forName("UTF-8"))));

        final ClientHandler clientHandler = new ClientHandler();
        acceptor.setHandler(clientHandler);

        acceptor.getSessionConfig().setReadBufferSize(2);
        acceptor.getSessionConfig().setIdleTime(IdleStatus.BOTH_IDLE, 10);
        acceptor.bind(new InetSocketAddress(hostName, 2222));

        Executors.newFixedThreadPool(1).submit(new Runnable() {
            @Override
            public void run() {
                for (;;) {
                    System.err.println("client count:" + (clientHandler.getCount().intValue() - clientHandler.last)
                            + " ,time=" + System.currentTimeMillis());
                    clientHandler.last = clientHandler.getCount().intValue();
                    try {
                        TimeUnit.SECONDS.sleep(1);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        });

    }

    private class ClientHandler extends IoHandlerAdapter {

        final AtomicInteger count = new AtomicInteger();

        public volatile int last;

        @Override
        public void messageReceived(IoSession session, Object message) throws Exception {
            count.addAndGet(1);
            //Msg msg = new Msg(message);
            //clientGroup.put(msg.getUid(), session);
            //notifyLogicServer(msg.getCmdId(), msg.toString());
        }
        
        public AtomicInteger getCount() {
            return count;
        }

        @Override
        public void exceptionCaught(IoSession session, Throwable cause) throws Exception {
            System.err.println("client "+cause.getMessage());
        }
    }

    private void notifyLogicServer(int cmdId, String message) {
        for (Entry<Integer, IoSession> entry : logicServers.entrySet()) {
            if (entry.getKey() == cmdId) {
                entry.getValue().write(message);
            }
        }
    }

    public static class Msg {
        private int uid;

        private int cmdId;

        public int getCmdId() {
            return cmdId;
        }

        public void setCmdId(int cmdId) {
            this.cmdId = cmdId;
        }

        private String msg;

        public Msg(Object msg) {
            String message = msg.toString();
            String[] ss = message.split(separator);
            this.cmdId = Integer.valueOf(ss[0]);
            this.uid = Integer.valueOf(ss[1]);
            this.msg = ss[2];
        }

        public Msg(int uid, int cmdId, String msg) {
            super();
            this.uid = uid;
            this.cmdId = cmdId;
            this.msg = msg;
        }

        public int getUid() {
            return uid;
        }

        public void setUid(int uid) {
            this.uid = uid;
        }

        public String getMsg() {
            return msg;
        }

        public void setMsg(String msg) {
            this.msg = msg;
        }

        @Override
        public String toString() {
            return cmdId + separator + uid + separator + msg;
        }

        public Msg() {
        }

    }

}
